// import com.alibaba.druid.DbType;
// import com.alibaba.druid.sql.ast.*;
// import com.alibaba.druid.sql.visitor.SQLASTVisitor;
// import com.alibaba.druid.util.MySqlUtils;

// import java.text.ParseException;
// import java.text.SimpleDateFormat;
// import java.util.*;

#include "SQLDateExpr.h"
#include "../SQLDataType.h"
#include "../SQLDataTypeImpl.h"
#include "../../../DbType.h"
#include <memory>
#include "../../../utils/MySqlUtils.h"
#include "../../visitor/SQLASTVisitor.h"

SQLDataType_ptr SQLDateExpr::DATA_TYPE = SQLDataType_ptr(new SQLDataTypeImpl(make_string_ptr("date")));
static std::list<DbType_ptr> tmp = {
    DbType_ptr(new DbType(&DbType::mysql)),
    DbType_ptr(new DbType(&DbType::oracle)),
    DbType_ptr(new DbType(&DbType::presto)),
    DbType_ptr(new DbType(&DbType::trino)),
    DbType_ptr(new DbType(&DbType::postgresql)),
    DbType_ptr(new DbType(&DbType::ads)),
    DbType_ptr(new DbType(&DbType::hive)),
    DbType_ptr(new DbType(&DbType::odps)),
    DbType_ptr(new DbType(&DbType::mariadb)),
    DbType_ptr(new DbType(&DbType::tidb))};

long SQLDateExpr::supportDbTypes = DbType::of(std::make_shared<std::list<DbType_ptr>>(tmp));

SQLDateExpr::SQLDateExpr()
{
}

SQLDateExpr::SQLDateExpr(string_ptr literal)
{
  this->setLiteral(literal);
}

SQLDateExpr::SQLDateExpr(long literal)
{
  this->setLiteral(literal);
}

SQLDateExpr::SQLDateExpr(long literal, string_ptr timeZone)
{
  this->setLiteral(literal, timeZone);
}

string_ptr SQLDateExpr::getLiteral()
{
  return literal;
}

void SQLDateExpr::setLiteral(string_ptr literal)
{
  this->literal = literal;
}

void SQLDateExpr::setLiteral(long x)
{
  setLiteral(x, NULL);
}

void SQLDateExpr::setLiteral(long x, string_ptr timeZone)
{
  // if (x == null) {
  //     this->literal = null;
  //     return;
  // }

  // SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
  // if (timeZone != null) {
  //     format.setTimeZone(timeZone);
  // }
  // gettimeofday(x, timeZone);
  char now[64];
  time_t tt;
  struct tm *ttime;
  // tt = atol(argv[1]);
  tt = x;
  // tt = 1212132599;  //uint
  // time(&tt);
  ttime = localtime(&tt);
  strftime(now, 64, "%Y-%m-%d %H:%M:%S", ttime);

  string_ptr text = make_string_ptr(now);
  setLiteral(text);
}

string_ptr SQLDateExpr::getValue()
{
  return literal;
}

std::shared_ptr<std::chrono::system_clock::time_point> SQLDateExpr::getDate()
{
  return getDate(NULL);
}

std::shared_ptr<std::chrono::system_clock::time_point> SQLDateExpr::getDate(string_ptr timeZone)
{
  return MySqlUtils::parseDate(literal, timeZone);
}

BOOL_ptr SQLDateExpr::addDay(int delta)
{
  if (literal->empty())
  {
    return BOOL::FALSE;
  }

  // SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
  // try {
  //     Date date = format.parse(literal);
  //     Calendar calendar = Calendar.getInstance();
  //     calendar.setTime(date);
  //     calendar.add(Calendar.DAY_OF_MONTH, delta);
  //     string_ptr  result_chars = format.format(calendar.getTime());
  //     setLiteral(result_chars);
  //     return BOOL::TRUE;
  // } catch (ParseException e) {
  //     // skip
  // }

  return BOOL::FALSE;
}

BOOL_ptr SQLDateExpr::addMonth(int delta)
{
  if (literal->empty())
  {
    return BOOL::FALSE;
  }

  // SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
  // try {
  //     Date date = format.parse(literal);
  //     Calendar calendar = Calendar.getInstance();
  //     calendar.setTime(date);
  //     calendar.add(Calendar.MONTH, delta);
  //     string_ptr  result_chars = format.format(calendar.getTime());
  //     setLiteral(result_chars);
  //     return BOOL::TRUE;
  // } catch (ParseException e) {
  //     // skip
  // }

  return BOOL::FALSE;
}

BOOL_ptr SQLDateExpr::addYear(int delta)
{
  if (literal->empty())
  {
    return BOOL::FALSE;
  }

  // SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
  // try {
  //     Date date = format.parse(literal);
  //     Calendar calendar = Calendar.getInstance();
  //     calendar.setTime(date);
  //     calendar.add(Calendar.YEAR, delta);
  //     string_ptr  result_chars = format.format(calendar.getTime());
  //     setLiteral(result_chars);
  //     return BOOL::TRUE;
  // } catch (ParseException e) {
  //     // skip
  // }

  return BOOL::FALSE;
}

//@Override
void SQLDateExpr::accept0(SQLASTVisitor_ptr visitor)
{
  visitor->visit(SharedObject(SQLDateExpr));
  visitor->endVisit(SharedObject(SQLDateExpr));
}

//@Override
int SQLDateExpr::hashCode()
{
  int prime = 31;
  int result = 1;
  // result = prime * result + ((literal.empty()) ? 0 : literal.hashCode());
  result = prime * result + ((literal->empty()) ? 0 : literal->length());
  return result;
}

//@Override
bool SQLDateExpr::equals(Object_ptr obj)
{
  if (this == obj.get())
  {
    return true;
  }
  if (obj == nullptr)
  {
    return false;
  }
  if (getClass() != obj->getClass())
  {
    return false;
  }
  std::shared_ptr<SQLDateExpr> other = std::dynamic_pointer_cast<SQLDateExpr>(obj);
  if (literal->empty())
  {
    if (!other->literal->empty())
    {
      return false;
    }
  }
  else if (literal != other->literal)
  {
    return false;
  }
  return true;
}

SQLObject_ptr SQLDateExpr::clone()
{
  SQLObject_ptr clone_tmp = std::shared_ptr<SQLDateExpr>(new SQLDateExpr());
  std::shared_ptr<SQLDateExpr> x = std::dynamic_pointer_cast<SQLDateExpr>(clone_tmp);
  // SQLDateExpr x = new SQLDateExpr();

  if (!this->literal->empty())
  {
    x->setLiteral(literal);
  }

  return x;
}

//@Override
BOOL_ptr SQLDateExpr::replace(SQLExpr_ptr expr, SQLExpr_ptr target)
{
  return BOOL::FALSE;
}

//@Override
SQLObject_list_ptr SQLDateExpr::getChildren()
{
  SQLObject_list_ptr tmp = std::make_shared<std::list<SQLObject_ptr>>();
  return tmp;
}

BOOL_ptr SQLDateExpr::isSupport(DbType_ptr dbType)
{
  return ((dbType->mask & supportDbTypes) != 0) ? BOOL::TRUE : BOOL::FALSE;
}

BOOL_ptr SQLDateExpr::check(string_ptr str)
{
  int len;
  if (str->empty() || (len = str->length()) < 8)
  {
    return BOOL::FALSE;
  }

  char c0 = str->at(0);
  char c1 = str->at(1);
  char c2 = str->at(2);
  char c3 = str->at(3);
  char c4 = str->at(4);
  char c5 = str->at(5);
  char c6 = str->at(6);
  char c7 = str->at(7);

  if (c0 < '1' | c0 > '9')
  {
    return BOOL::FALSE;
  }
  if (c1 < '0' | c1 > '9')
  {
    return BOOL::FALSE;
  }
  if (c2 < '0' | c2 > '9')
  {
    return BOOL::FALSE;
  }
  if (c3 < '0' | c3 > '9')
  {
    return BOOL::FALSE;
  }

  int year = (c0 - '0') * 1000 + (c1 - '0') * 100 + (c2 - '0') * 10 + (c3 - '0');
  if (year < 1000)
  {
    return BOOL::FALSE;
  }

  if (c4 != '-')
  {
    return BOOL::FALSE;
  }

  if (c5 < '0' | c5 > '9')
  {
    return BOOL::FALSE;
  }

  int month, day;
  if (c6 == '-')
  {
    month = (c5 - '0');

    if (c7 < '0' | c7 > '9')
    {
      return BOOL::FALSE;
    }

    if (len == 8)
    {
      day = c7 - '0';
    }
    else if (len == 9)
    {
      char c8 = str->at(8);
      if (c8 < '0' | c8 > '9')
      {
        return BOOL::FALSE;
      }
      day = (c7 - '0') * 10 + (c8 - '0');
    }
    else
    {
      return BOOL::FALSE;
    }
  }
  else if (c6 < '0' | c6 > '9')
  {
    return BOOL::FALSE;
  }
  else
  {
    month = (c5 - '0') * 10 + (c6 - '0');

    if (c7 != '-')
    {
      return BOOL::FALSE;
    }

    if (len == 9)
    {
      char c8 = str->at(8);
      if (c8 < '0' | c8 > '9')
      {
        return BOOL::FALSE;
      }
      day = c8 - '0';
    }
    else if (len == 10)
    {
      char c8 = str->at(8);
      char c9 = str->at(9);
      if (c8 < '0' | c8 > '9')
      {
        return BOOL::FALSE;
      }
      if (c9 < '0' | c9 > '9')
      {
        return BOOL::FALSE;
      }
      day = (c8 - '0') * 10 + (c9 - '0');
    }
    else
    {
      return BOOL::FALSE;
    }
  }

  if (month < 1)
  {
    return BOOL::FALSE;
  }

  if (day < 1)
  {
    return BOOL::FALSE;
  }

  switch (month)
  {
  case 1:
  case 3:
  case 5:
  case 7:
  case 8:
  case 10:
  case 12:
    if (day > 31)
    {
      return BOOL::FALSE;
    }
    return BOOL::TRUE;
  case 2:
    if (day > 29)
    {
      return BOOL::FALSE;
    }
    return BOOL::TRUE;
  case 4:
  case 6:
  case 9:
  case 11:
    if (day > 30)
    {
      return BOOL::FALSE;
    }
    return BOOL::TRUE;
  default:
    return BOOL::FALSE;
  }
}

// string_ptr  SQLDateExpr::format(Calendar calendar) {
//     if (calendar == null) {
//         return null;
//     }

//     int year = calendar.get(Calendar.YEAR);
//     int month = calendar.get(Calendar.MONTH) + 1;
//     int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);

//     char[] chars = new char[10];
//     chars[0] = (char) (year / 1000 + '0');
//     chars[1] = (char) ((year / 100) % 10 + '0');
//     chars[2] = (char) ((year / 10) % 10 + '0');
//     chars[3] = (char) (year % 10 + '0');
//     chars[4] = '-';
//     chars[5] = (char) (month / 10 + '0');
//     chars[6] = (char) (month % 10 + '0');
//     chars[7] = '-';
//     chars[8] = (char) (dayOfMonth / 10 + '0');
//     chars[9] = (char) (dayOfMonth % 10 + '0');

//     return new string_ptr (chars);
// }

SQLDataType_ptr SQLDateExpr::computeDataType()
{
  return DATA_TYPE;
}
